/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | foam-extend: Open Source CFD
   \\    /   O peration     | Version:     4.1
    \\  /    A nd           | Web:         http://www.foam-extend.org
     \\/     M anipulation  | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
	This file is part of foam-extend.

	foam-extend is free software: you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the
	Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	foam-extend is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with foam-extend.  If not, see <http://www.gnu.org/licenses/>.

Description

	Reader for .ac files generated by AC3D.

	See http://www.ac3d.org/ac3d/man/ac3dfileformat.html

\*---------------------------------------------------------------------------*/

#include "triSurface.H"
#include "IFstream.H"
#include "IStringStream.H"
#include "transform.H"
#include "tensor.H"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{

// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

static label parseInt(const string& str)
{
	IStringStream intStream(str);

	label a;

	intStream >> a;

	return a;
}


static bool readCmd(IFstream& ACfile, string& cmd, string& args)
{
	if (ACfile.good())
	{
		string line;
		ACfile.getLine(line);

		string::size_type space = line.find(' ');

		if (space != string::npos)
		{
			cmd  = line.substr(0, space);
			args = line.substr(space+1);

			return true;
		}
	}
	return false;
}


// Read up to line starting with cmd. Sets args to rest of line.
// Returns true if found, false if stream is not good anymore.
static bool readUpto
(
	const string& cmd,
	IFstream& ACfile,
	string& args
)
{
	while (ACfile.good())
	{
		string line;
		ACfile.getLine(line);

		string::size_type space = line.find(' ');

		if (space != string::npos && line.substr(0, space) == cmd)
		{
			args = line.substr(space+1);
			return true;
		}
	}
	return false;
}


// Likewise but throws error if cmd not found
static void readUpto
(
	const string& cmd,
	IFstream& ACfile,
	string& args,
	const string errorMsg
)
{
	if (!readUpto(cmd, ACfile, args))
	{
		FatalErrorIn("triSurface::readAC(const fileName&)")
			<< "Cannot find command " << cmd
			<< errorMsg << exit(FatalError);
	}
}


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

bool triSurface::readAC(const fileName& ACfileName)
{
	IFstream ACfile(ACfileName);

	if (!ACfile.good())
	{
		FatalErrorIn("triSurface::readAC(const fileName&)")
			<< "Cannot read file " << ACfileName
			<< exit(FatalError);
	}

	string line;
	ACfile.getLine(line);

	string version = line.substr(4);

	if (version != "b")
	{
		WarningIn("bool triSurface::readAC(const fileName& ACfileName)")
			<< "When reading AC3D file " << ACfileName
			<< " read header " << line << " with version " << version
			<< endl << "Only tested reading with version 'b'."
			<< " This might give problems" << endl;
	}

	string cmd;

	string args;

	if (!readUpto("OBJECT", ACfile, args) || (args != "world"))
	{
		FatalErrorIn("bool triSurface::readAC(const fileName& ACfileName)")
			<< "Cannot find \"OBJECT world\" in file " << ACfileName
			<< exit(FatalError);
	}

	// Number of kids = patches

	readUpto("kids", ACfile, args, "");

	label nPatches = parseInt(args);

	// Storage for patches and unmerged points and faces

	DynamicList<point> points;
	DynamicList<labelledTri> faces;
	geometricSurfacePatchList patches(nPatches);


	// Start of vertices for object/patch
	label patchStartVert = 0;

	for (label patchI = 0; patchI < nPatches; patchI++)
	{
		readUpto("OBJECT", ACfile, args, " while reading patch " + name(patchI));

		// Object global values
		string patchName = string("patch") + name(patchI);
		label nVerts = 0;
		tensor rot(I);
		vector loc(0, 0, 0);

		// Read all info for current patch
		while (ACfile.good())
		{
			// Read line and get first word. If end of file break since
			// patch should always end with 'kids' command ?not sure.
			if (!readCmd(ACfile, cmd, args))
			{
				FatalErrorIn("triSurface::readAC(const fileName&)")
					<< "Did not read up to \"kids 0\" while reading patch "
					<< patchI << " from file " << ACfileName
					<< exit(FatalError);
			}

			if (cmd == "name")
			{
				IStringStream nameStream(args);

				nameStream >> patchName;
			}
			else if (cmd == "rot")
			{
				// rot %f %f %f  %f %f %f  %f %f %f
				IStringStream lineStream(args);

				lineStream
					>> rot.xx() >> rot.xy() >> rot.xz()
					>> rot.yx() >> rot.yy() >> rot.yz()
					>> rot.zx() >> rot.zy() >> rot.zz();

				WarningIn("triSurface::readAC(const fileName&)")
					<< "rot (rotation tensor) command not implemented"
					<< "Line:" << cmd << ' ' << args << endl
					<< "while reading patch " << patchI << endl;
			}
			else if (cmd == "loc")
			{
				IStringStream lineStream(args);

				lineStream >> loc.x() >> loc.y() >> loc.z();
			}
			else if (cmd == "numvert")
			{
				nVerts = parseInt(args);

				for (label vertI = 0; vertI < nVerts; vertI++)
				{
					ACfile.getLine(line);

					IStringStream lineStream(line);

					point pt;

					lineStream >> pt.x() >> pt.y() >> pt.z();

					// Offset with current translation vector
					points.append(pt+loc);
				}
			}
			else if (cmd == "numsurf")
			{
				label nTris = parseInt(args);

				for (label triI = 0; triI < nTris; triI++)
				{
					static string errorMsg =
					    string(" while reading face ")
					  + name(triI) + " on patch " + name(patchI)
					  + " from file " + ACfileName;

					readUpto("SURF", ACfile, args, errorMsg);
					readUpto("mat", ACfile, args, errorMsg);
					readUpto("refs", ACfile, args, errorMsg);

					label size = parseInt(args);

					if (size != 3)
					{
					    FatalErrorIn("triSurface::readAC(const fileName&)")
					        << "Can only read surfaces with 3 vertices."
					        << endl
					        << "Detected " << size << " when reading triangle "
					        << triI << " of patch " << patchI
					        << exit(FatalError);
					}

					ACfile.getLine(line);

					label v0 = parseInt(line);

					ACfile.getLine(line);

					label v1 = parseInt(line);

					ACfile.getLine(line);

					label v2 = parseInt(line);

					faces.append
					(
					    labelledTri
					    (
					        v0 + patchStartVert,
					        v1 + patchStartVert,
					        v2 + patchStartVert,
					        patchI
					    )
					);
				}

				// Done the current patch. Increment the offset vertices are
				// stored at
				patchStartVert += nVerts;
			}
			else if (cmd == "kids")
			{
				// 'kids' denotes the end of the current patch.

				label nKids = parseInt(args);

				if (nKids != 0)
				{
					FatalErrorIn("triSurface::readAC(const fileName&)")
					    << "Can only read objects without kids."
					    << " Encountered " << nKids << " kids when"
					    << " reading patch " << patchI
					    << exit(FatalError);
				}

				patches[patchI] =
					geometricSurfacePatch
					(
					    "empty",
					    word(patchName),
					    patchI
					);

				// Stop reading current patch
				break;
			}
		}
	}

	faces.shrink();

	// Transfer DynamicLists to straight ones.
	pointField allPoints(points.xfer());

	*this = triSurface(faces, patches, allPoints, true);

	stitchTriangles(allPoints);

	return true;
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

} // End namespace Foam

// ************************************************************************* //
